/***************************    WARNING    **********************************/
/* Do not modify the code below these markers.
 * All the code is JSwarm Engine code and must remain  
 * Separate from all actual game code!!!
 * 
	/****************************************************************************/

package main;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.ImageObserver;
import java.nio.DoubleBuffer;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.JPanel;
import java.awt.Canvas;
import java.awt.image.BufferedImage;

import performanceProfiling.PerformanceProfiling;
import quadtree.AbstractNodeElement;
import quadtree.AbstractNode.Cell;
import quadtree.point.PointNode;
import quadtree.point.PointNodeElement;
import quadtree.point.PointQuadTree;
import networking.MultiPlayer;
import java.awt.image.BufferStrategy;
import java.awt.BufferCapabilities;
import java.awt.GraphicsConfiguration;





@SuppressWarnings("serial")
public class Board extends JPanel implements Commons,Runnable{ 


	private Thread animator;
	private long animatorId;
	private Thread repaint;
	private long repaintId;
	private Thread input;
	private long inputId;
	ExecutorService multiPlayerExecSvc;
	private Dimension d;

	protected TAdapter userInput;
	private PerformanceProfiling profiles;
	private boolean debug;
	private boolean cycleA, cycleB;
	
	
	public GameTree gameTree;
	
	public MultiPlayer multiPlayer;

	private Boolean readyToRenderLock = true;
	private BufferedImage offImg1;
	private BufferedImage offImg2;
	private BufferedImage offImg;
	
	

	
	public Board() 
	{	
		this.setVisible(true);
		gameTree = new GameTree();
		userInput = new TAdapter();
		addKeyListener(userInput);
		addMouseListener(userInput);
		addMouseMotionListener(userInput);
		multiPlayerExecSvc = Executors.newFixedThreadPool(2);
		setFocusable(true);
		d = new Dimension(BOARD_WIDTH, BOARD_HEIGTH);
		this.setSize(d);
		setBackground(Color.black);
		cycleA = true;
		cycleB = false;	
		profiles = new PerformanceProfiling();
		profiles.showAll();	

		if (animator == null) {
			animator = new Thread(this);
			animatorId = animator.getId();
			animator.start();
		}
		if (repaint == null) {
			repaint = new Thread(this);
			repaintId = repaint.getId();
			repaint.start();
		}		
		
		/*********NETWORKING********/
		//multiPlayer = new MultiPlayer(9999, "2ua1010y62");		
		/*********NETWORKING********/	
	}

	
	public Board(int width, int height, Color bgColor) 
	{	
		this.setVisible(true);
		gameTree = new GameTree();
		userInput = new TAdapter();
		addKeyListener(userInput);
		addMouseListener(userInput);
		addMouseMotionListener(userInput);
		multiPlayerExecSvc = Executors.newFixedThreadPool(2);
		setFocusable(true);
		d = new Dimension(width, height);
		this.setSize(d);
		setBackground(bgColor);
		cycleA = true;
		cycleB = false;	
		profiles = new PerformanceProfiling();
		profiles.showAll();
		
		if (animator == null) {
			animator = new Thread(this);
			animatorId = animator.getId();
			animator.start();
		}
		if (repaint == null) {
			repaint = new Thread(this);
			repaintId = repaint.getId();
			repaint.start();
		}		
		/*********NETWORKING********/
		//multiPlayer = new MultiPlayer(9999, "2ua1010y62");		
		/*********NETWORKING********/	
	}

	public void setDebug(boolean debugValue) {
		this.debug = debugValue;
		gameTree.setDebug(debugValue);
	}
	
	public Dimension getD() {
		return d;
	}

	public void addNotify() {
		super.addNotify();
	}
	
	protected void setCursorImage(String imageLocation) {
		this.setCursor(Toolkit.getDefaultToolkit().createCustomCursor(
				Toolkit.getDefaultToolkit().getImage(imageLocation), new Point(0,0), "Custom Cursor"));
	}

	public Graphics2D createBuffer() {
	    Graphics2D g2 = null;
	    offImg = offImg1;
	    if(cycleA){
	    	offImg = offImg2;
	    }	    
	    if (offImg == null || offImg.getWidth() != getSize().width
	        || offImg.getHeight() != getSize().height) {
	      offImg = (BufferedImage) createImage(getSize().width, getSize().height);
	    }
	    if (offImg != null) {
	      g2 = offImg.createGraphics();
	      g2.setBackground(getBackground());
	    }
	    if(g2 != null){
	    	g2.clearRect(0, 0, getSize().width, getSize().height);
	    }	 
	    return g2;
	 
	  }
	
	public synchronized void render(){
		while(true){
			try{				
				wait();		
			}catch(InterruptedException e){}
			Graphics2D g2d = createBuffer();
			if(g2d != null){
				synchronized(readyToRenderLock){
					super.paint(g2d);
					ImageObserver image_observer = this;
					g2d.setRenderingHints(text_antialising);
					g2d.setRenderingHints(alpha_interp);
					g2d.setRenderingHints(key_rendering);
					g2d.setRenderingHints(key_color_rendering);
					gameTree.drawEveryThing(g2d, image_observer, cycleA);
					if(debug){
						profiles.printToScreen(g2d, 0, 0);
					}
					g2d.dispose();
					if (offImg != null && isShowing()) {
						getGraphics().drawImage(offImg, 0, 0, this);
					}
				}
			}			
		}
	}
	public void paint(Graphics g){
	
	}


	public void handleInput(){
		//Function not needed.  Only as a place holder
		//is referenced in animationCycle but is never called due to
		//boards that extend need to write this function themselves
		try{
			//gameHandleInput();
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}

	public void animationCycle(long time)  {	
		handleInput();
		synchronized(readyToRenderLock){
			gameTree.thinkEverything(cycleA, time);
		}		
	}

	public synchronized void notifyReDraw(){		
		notifyAll();
	}
	public void run() {
		if(Thread.currentThread().getId() == repaintId){			
				render();				
		}
		
		if(Thread.currentThread().getId() == animatorId){
			long beforeTime, timeDiff, sleep;
			timeDiff = 0;
			beforeTime = System.currentTimeMillis();
			while(true){
			
				notifyReDraw();	
				try{	
					if(debug){
						profiles.incrementAnimationCycle();
						profiles.fpsStart();
						profiles.thinkTimeStart();
					}
					animationCycle(timeDiff);
					
					if(debug){
						profiles.thinkTimeStop();
						profiles.fpsStop();
					}
					
				}catch(Exception e)	{
					e.printStackTrace();
				}
				if(debug){
					profiles.drawTimeStart();
				}		
				if(debug){
					profiles.drawTimeStop();
				}
				timeDiff = System.currentTimeMillis() - beforeTime;
				sleep = DELAY - timeDiff;
	
				if (sleep < 0) sleep = 2;
				try{								
					Thread.sleep(sleep);
					cycleA = !cycleA;
					cycleB = !cycleB;
					
				} catch (InterruptedException e) {
					System.out.println("interrupted");
				}
				beforeTime = System.currentTimeMillis();
			}
		}
	}		
}//end Class

